
(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define([], factory);
    } else if (typeof module === 'object' && module.exports) {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like environments that support module.exports,
        // like Node.
        module.exports = factory();
    } else {
        // Browser globals (root is window)
        root.AltScroller = factory();
  }
}(typeof self !== 'undefined' ? self : this, function () {

	function Events() {
		this.events = {};
	}
	Events.prototype = {
		$emit: function $emit(name, data){
			let cbs = this.events[name];
			if(!cbs || !cbs.length) return;
			for(let i = 0, l = cbs.length; i < l; i++){
				cbs[i].call(this, data);
			}
		},
		$on: function $on(name, cb){
			if(name === undefined) return;
			let cbs = this.events[name];
			if(!cbs){
				cbs = [];
				this.events[name] = cbs;
			}
			cbs.push(cb);
		},
		$off: function $off(name, cb){
			if(name === undefined){
				this.events = {};
				return;
			}
			let cbs = this._events[name];
			let index = this._events.indexOf(cb);
			if(index > -1){
				cbs.splice(index, 1);
			}
		}
	}

	function AltScroller(options){
		let defOpts = {
			width: 0,
            height: 0,
            
            preventDefault: true
		}
        let opts = Object.assign({}, defOpts, options);
        this.opts = opts;

        if(opts.el instanceof HTMLElement){
            this.$el = opts.el;
        }else{
            this.$el = document.querySelector(opts.el);
        }
		
		this.$el.style.overflow = 'hidden';

		this._events = new Events();
		this.$on = this._events.$on.bind(this._events);
		this.$emit = this._events.$emit.bind(this._events);
		this.$off = this._events.$off.bind(this._events);

		this.scrollLeft = 0;
		this.scrollTop = 0;

		this.viewInfo = {
			boxWidth: 0,
			boxHeight: 0,
			contentWidth: 0,
			contentHeight: 0,

			maxScrollLeft: 0,
			maxScrollTop: 0
		}

		if(opts.boxWidth === undefined){
			opts.boxWidth = this.$el.offsetWidth;
		}
		if(opts.boxHeight === undefined){
			opts.boxHeight = this.$el.offsetHeight;
		}
		if(opts.contentWidth === undefined){
			opts.contentWidth = this.$el.scrollWidth;
		}
		if(opts.contentHeight === undefined){
			opts.contentHeight = this.$el.scrollHeight;
		}

		this.setDimensions(
			opts.boxWidth, 
			opts.boxHeight, 
			opts.contentWidth, 
			opts.contentHeight
		);

		this.onWheel = this._onWheel.bind(this);
		this.addEvents();
	}

	AltScroller.prototype = Object.assign({
		setDimensions: function setDimensions(boxWidth, boxHeight, contentWidth, contentHeight){
            let maxScrollLeft = contentWidth - boxWidth;
            let maxScrollTop = contentHeight - boxHeight;
            this.viewInfo = {
				boxWidth: boxWidth,
				boxHeight: boxHeight,
				contentWidth: contentWidth,
				contentHeight: contentHeight,

				maxScrollLeft: maxScrollLeft < 0 ? 0 : maxScrollLeft,
				maxScrollTop: maxScrollTop < 0 ? 0 : maxScrollTop
            }
            
            this._scrollTo(this.scrollLeft, this.scrollTop);
        },
        scrollTo: function scrollTo(x, y, notTriggerEvent){
            this._scrollTo(x, y, {}, notTriggerEvent);
        },
        _scrollTo: function _scrollTo(x, y, e, notTriggerEvent){
            let maxScrollLeft = this.viewInfo.maxScrollLeft;
            let maxScrollTop = this.viewInfo.maxScrollTop;
            if(x > maxScrollLeft){
                x = maxScrollLeft;
            }else if(x < 0){
                x = 0;
            }
            if(y > maxScrollTop){
                y = maxScrollTop;
            }else if(y < 0){
                y = 0;
            }
            if(x === this.scrollLeft && y === this.scrollTop){
                return;
            }

            let deltaX = x - this.scrollLeft;
            let deltaY = y - this.scrollTop;
            this.scrollLeft = x;
            this.scrollTop = y;

            if(notTriggerEvent) return;
            this.$emit('scroll', {
                scrollLeft: x,
                scrollTop: y,
                deltaX,
                deltaY,
                originEvent: e
            });
        },

		addEvents: function addEvents(){
			this.$el.addEventListener('wheel', this.onWheel);
		},
		removeEvents: function removeEvents(){
			this.$el.removeEventListener('wheel', this.onWheel);
		},

		_onWheel: function _onWheel(event){
            let scrollLeft = this.scrollLeft;
			let scrollTop = this.scrollTop;

			scrollTop += event.deltaY;
            scrollLeft += event.deltaX;
            this._scrollTo(scrollLeft, scrollTop);

            if(this.opts.preventDefault){
                event.preventDefault();
                event.stopPropagation();
            }
		},

		destroy: function destroy(){
            this.removeEvents();
            this.$off();
		}
	});
    
    return AltScroller;
}));